package com.lemon.exam.common.handler;

import com.lemon.exam.common.enums.CodeEnum;
import com.lemon.exam.common.exception.CustomException;
import com.lemon.exam.common.util.ResponseUtil;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 全局异常处理
 *
 * @author Lemon
 * @since 2024/02/7 14:52
 */
@Component
@Order(-2) // 确保优先级高于默认的 WebFlux 异常处理器
public class GlobalExceptionHandler implements WebExceptionHandler {
    @Override
    @NonNull
    public Mono<Void> handle(@NonNull ServerWebExchange exchange, @NonNull Throwable e) {
        // 打印异常信息
        e.printStackTrace();

        if (e instanceof CustomException ce) {//自定义异常
            Integer code = ce.getCode();
            if (code != null) {
                return response(exchange, e, code);
            } else {
                return response(exchange, e);
            }
        } else if (e instanceof IllegalArgumentException) {
            return response(exchange, e, CodeEnum.ERROR_400);
        } else if (e instanceof WebExchangeBindException ex) {// 参数校验失败
            // 提取第一个错误信息
            String message = ex.getFieldErrors().parallelStream()
                    .findFirst()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .orElse("参数校验失败");
            return response(exchange, e, CodeEnum.ERROR_400.getCode(), message);
        } else {
            return response(exchange, e);
        }
    }

    /**
     * 响应异常
     *
     * @param exchange
     * @param e
     * @param codeEnum
     * @return
     */
    private Mono<Void> response(ServerWebExchange exchange, Throwable e, CodeEnum codeEnum) {
        return response(exchange, e, codeEnum.getCode(), codeEnum.getMessage());
    }

    /**
     * 响应异常
     *
     * @param exchange
     * @param e
     * @param code
     * @return
     */
    private Mono<Void> response(ServerWebExchange exchange, Throwable e, int code) {
        return response(exchange, e, code, null);
    }

    /**
     * 响应异常
     *
     * @param exchange
     * @param e
     * @return
     */
    private Mono<Void> response(ServerWebExchange exchange, Throwable e) {
        String[] split = e.getMessage().split(" ");
        if (split.length > 1 && StringUtils.hasText(split[0].trim()) && split[0].trim().matches("\\d+")) {
            int code = Integer.parseInt(split[0].trim());
            String message = e.getMessage().replaceFirst(split[0].trim(), "").trim();
            CodeEnum codeEnum = CodeEnum.getByCode(code);
            if (codeEnum != null) {
                return response(exchange, e, codeEnum);
            }
            return response(exchange, e, code, message);
        }
        return response(exchange, e, CodeEnum.ERROR_500.getCode());
    }

    /**
     * 响应异常
     *
     * @param exchange
     * @param e
     * @param code
     * @param message
     * @return
     */
    private Mono<Void> response(ServerWebExchange exchange, Throwable e, int code, String message) {
        //1.设置响应信息
        if (!StringUtils.hasText(message)) {
            message = StringUtils.hasText(e.getMessage()) ? e.getMessage() : "系统异常！";
        }
        //2.判断是否是sse请求
        List<MediaType> accept = exchange.getRequest().getHeaders().getAccept();
        if (!CollectionUtils.isEmpty(accept) && accept.contains(MediaType.TEXT_EVENT_STREAM)) {
            return ResponseUtil.sse(exchange.getResponse(), code, message);
        }
        //3.设置响应码(状态码是4开头的响应成功)
        return ResponseUtil.err(exchange.getResponse(), code, message);
    }
}
